import ij.IJ;
import ij.ImagePlus;
import ij.gui.NewImage;
import ij.io.FileSaver;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
import java.util.Random; 



import pluginUtils.Circle;
import pluginUtils.Circle3D;
import pluginUtils.CircleArrangement;
import pluginUtils.Complexe; 
import pluginUtils.DoyleCircle;
import pluginUtils.Droite;
import pluginUtils.DupinCyclide;
import pluginUtils.MobiusTransform;
import pluginUtils.CircleInversion;
import pluginUtils.PackCircle;
import pluginUtils.Plane;
import pluginUtils.Point2D;
import pluginUtils.Pos3D;
import pluginUtils.Sphere;
import pluginUtils.SphereInversion;
import pluginUtils.Vector2D;
import sliceformUtils.CoupleDAngles;
import sliceformUtils.DroiteSpace3D;
import sliceformUtils.ImagePlan;
import sliceformUtils.Ligne;
import sliceformUtils.Mesh;
import sliceformUtils.STLReader;
import sliceformUtils.Saucisson;
import sliceformUtils.Segment;
import sliceformUtils.Triangle;


public class CyclideDupinV2 extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	
	private PrintStream output;
	private double xShift=0; 
	private double zShift=0; 
	
	
	
	private Random generator=new Random(); // One always needs some randomness
	
	// Scaling  the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	private void drawPoint(double x,double y,ImageProcessor ip){
		int intX=(int)(x*acoef+ccoef);
		int intY=(int)(y*vcoef+wcoef);
		ip.fillOval(intX-10,intY-10,20,20);
	}
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	private void drawLine(double xd,double yd,double xf,double yf,ImageProcessor ip){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		ip.drawLine(XD,YD,XF,YF); 
	}
	
	private void drawLine(Point2D a,Point2D b,ImageProcessor ip){
		drawLine(a.getX(),a.getY(),b.getX(),b.getY(),ip); 
	}
	
	private void drawCircle(Vector2D center,double rayon,ImageProcessor ip){
		double xCenter=center.getX(); 
		double yCenter=center.getY(); 
		int intX=(int)((xCenter-xShift)*acoef+ccoef);
		int intY=(int)((yCenter-zShift)*vcoef+wcoef);
		int intRx=(int)(rayon*acoef);
		int intRy=(int)(rayon*vcoef);
		ip.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);
	}
	
	
	/* Trouver l'intersection de la droite avec un des quatres cotes de l'image
	 * retourne null si parallele ou en dehors de l'image. 
	 * on ne s'occupe pas de la valeur de x (tout se passe dans le plan perpendiculaire
	 * a l'axe Ox)
	 */
	public Pos3D coteVsDroite(Pos3D A,Pos3D B,DroiteSpace3D D){
		double yv=D.getDirection().getY(); 
		double zv=D.getDirection().getZ(); 
		double ya=A.getY(); 
		double za=A.getZ(); 
		double yb=B.getY(); 
		double zb=B.getZ(); 
		double y0=D.getPoint().getY(); 
		double z0=D.getPoint().getZ(); 
		double det=-zv*(ya-yb)+yv*(za-zb); 
		if(Math.abs(det)<1e-6) return null; 
		double detAlpha=-zv*(y0-yb)+yv*(z0-zb);
		double alpha=detAlpha/det; 
		if ((alpha<0)||(alpha>1)) return null; 
		// on s'en fout de X, on va tracer une droite dans un plan
		double ny=alpha*ya+(1-alpha)*yb; 
		double nz=alpha*za+(1-alpha)*zb;
		return new Pos3D(0,ny,nz); 
	}
		
	
	/** Calcule les angles de rotation amenant un plan parallele a un plan perpendiculaire a ox **/
	
	public CoupleDAngles rotations(Plane p){
		CoupleDAngles resu=new CoupleDAngles(); 
		if(p.getA()==0){
			if(p.getB()==0){
				resu.setAlpha(0); 
				if(p.getC()<=0) resu.setBeta(Math.PI/2); else resu.setBeta(-Math.PI/2); 
				System.out.println("resu cas 1 "+resu); 
				return resu;
			}
			else // A=0 et B!=0
			 {
			 if(p.getB()<0) resu.setAlpha(Math.PI/2); else resu.setAlpha(-Math.PI/2);
			 }
			 }
	    else // A!=0;
	    {
	    	resu.setAlpha(-Math.atan2(p.getB(),p.getA())); 
	    	/*
	    	double roty=Math.atan(p.getB()/p.getA()); 
			if(roty<0) roty=-roty; 
	    	resu.setAlpha(roty);
	    	*/
	    	} 
		// On s'occupe de beta
		double leng=Math.sqrt(p.getA()*p.getA()+p.getB()*p.getB()); 
		resu.setBeta(-Math.atan2(p.getC(),leng));  
		System.out.println("resu cas 2 "+resu); 
		return resu; 
		}
		
	
	/*
	 * 
	 * 
	 * 
	 * CoupleDAngles larot=rotations(plansTheta[i]); 
				DroiteSpace3D coupureRot=coupure.rotate(larot); 
				ArrayList<Sommet> lesBords=intersectCadre(lmoins,limit,lmoins,limit, coupureRot);
				if(lesBords.size()==2){ // tracer la ligne
					Sommet s1=lesBords.get(0); 
					Sommet s2=lesBords.get(1);
					drawLine(s1.getY(),s1.getZ(),s2.getY(),s2.getZ(),i1); 
				}// if lesBords
	 */
	
	
	public ArrayList<Pos3D> intersectCadre(double ymin,double ymax,double zmin,double zmax,DroiteSpace3D Q){
		ArrayList<Pos3D> resu=new ArrayList<Pos3D>(); 
		Pos3D A=new Pos3D(0,ymin,zmin); 
		Pos3D B=new Pos3D(0,ymin,zmax); 
		Pos3D C=new Pos3D(0,ymax,zmin); 
		Pos3D D=new Pos3D(0,ymax,zmax); 
		if(coteVsDroite(A,B,Q)!=null) resu.add(coteVsDroite(A,B,Q)); 
		if(coteVsDroite(A,C,Q)!=null) resu.add(coteVsDroite(A,C,Q)); 
		if(coteVsDroite(B,D,Q)!=null) resu.add(coteVsDroite(B,D,Q)); 
		if(coteVsDroite(C,D,Q)!=null) resu.add(coteVsDroite(C,D,Q)); 
		return resu; 
	}
	
	public void dessiner(ImagePlan impl,ImageProcessor i1){
		Plane lePlan=impl.getLePlan(); 
		CoupleDAngles larot=rotations(lePlan); 
		CoupleDAngles lrbis=new CoupleDAngles(larot.getAlpha(),larot.getBeta()); 
		if(lrbis.getAlpha()<0) lrbis.setAlpha(-lrbis.getAlpha()); 
		Iterator<DroiteSpace3D> id=impl.iterator(); 
		while(id.hasNext()){
			DroiteSpace3D courante=id.next(); 
			DroiteSpace3D coupureRot=courante.rotate(lrbis); 
			ArrayList<Pos3D> lesBords=intersectCadre(x0,x1,y0,y1,coupureRot);
			if(lesBords.size()==2){ // tracer la ligne
				Pos3D s1=lesBords.get(0); 
				Pos3D s2=lesBords.get(1);
				i1.setLineWidth(2); 
				i1.setColor(Color.LIGHT_GRAY); 
				drawLine(s1.getY(),s1.getZ(),s2.getY(),s2.getZ(),i1); 
			}// if lesBords
			i1.setLineWidth(4); 
			i1.setColor(Color.BLACK); 
		}
		// Dessiner le cercle
		// remettre le centre dans le bon plan
		System.out.println("Plan du cercle "+lePlan); 
		System.out.println("Cercle initial avant rotation : "+impl.getCercle().getCenter());
	    
		Pos3D newCenter=impl.getCercle().getCenter().rotate(larot); 
		double rayon=impl.getCercle().getRayon(); 
		System.out.println(larot); 
		System.out.println("Centre --> "+newCenter+"\n"+"Rayon : "+rayon); 
		drawCircle(new Vector2D(newCenter.getY(),newCenter.getZ()),rayon,i1);  // debug : 0 a la place de getZ
		Circle3D secCir=impl.getSecondCircle(); 
		if(secCir!=null){
			Pos3D c2c=secCir.getCenter().rotate(larot); 
			double radi=secCir.getRayon(); 
			drawCircle(new Vector2D(c2c.getY(),c2c.getZ()),radi,i1);
		}
	}
	
	public void run(String arg) {
	
		double a=5; 
		double mu=2; 
		double c=1; 
		
		int nbTheta=8; 
		int nbAlpha=10; 
		
		primeIP = new ColorProcessor(X,Y);
		
		// Tout tient dans une image carree
		double limit=2*(a+mu); 
		double lmoins=-2*(a+mu);
	
		setBounds(lmoins,limit,lmoins,limit); 
		setCoefs(); 
		
		
		
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		
		primeIP.fillPolygon(background);
		
		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.GREEN);
		primeIP.setLineWidth(3); 
		drawLine(-limit,0,limit,0); 
		drawLine(0,-limit,0,limit); 

		setProcessor("Verifying", primeIP);
		
		
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(5);
		drawLine(0,-limit/2,limit/6,2.0*limit/3); 
		
		DupinCyclide dc=new DupinCyclide(a, mu, c);
		/*
		for(int j=0;j<nbTheta;j++){
		ImagePlus gamma=NewImage.createRGBImage ("/tmp/cyclide/theta"+j,X,Y, 1, NewImage.FILL_WHITE);	
		ImageProcessor i1=gamma.getProcessor();
		FileSaver fs=new FileSaver(gamma); 
		i1.setColor(Color.BLACK);
		i1.setLineWidth(5);
		
		Circle3D origine=dc.cercleTheta(2*j*Math.PI/nbTheta);
		System.out.println("Rayon : "+origine.getRayon()); 
		ImagePlan essai=new ImagePlan(origine); 
		
		
		for(int i=0;i<nbAlpha;i++)
		{
			Circle3D courant=dc.cercleAlpha(2*i*Math.PI/nbAlpha); 
			Plane secant=courant.getPlan(); 
			DroiteSpace3D trace=Plane.inter(secant, origine.getPlan()); 
			essai.add(trace); 
		}
	
		dessiner(essai,i1); 
		fs.saveAsPng("/tmp/cyclide/theta"+j+".png"); 
		
		gamma.show();
		gamma.close(); 
		}
		*/
		// les cercles alpha
		
		Circle3D origine=dc.cercleAlpha(Math.PI/2);
		System.out.println("Rayon : "+origine.getRayon()); 
		ImagePlan essai=new ImagePlan(origine); 
		for(int i=0;i<nbTheta;i++)
		{
			Circle3D courant=dc.cercleTheta(2*i*Math.PI/nbTheta); 
			Plane secant=courant.getPlan(); 
			DroiteSpace3D trace=Plane.inter(secant, origine.getPlan()); 
			essai.add(trace); 
		}
		essai.setSecondCircle(dc.cercleAlpha(3*Math.PI/2)); 
		dessiner(essai,primeIP);
		
		
		show(); 
		
		
		
		System.out.println("fini"); 
		
		
	
}
	
}
	